package com.bbex.config.iplimit;

import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;

import com.bbex.config.sercurity.SecurityConfig;
import com.bbex.pojo.GlobalMessageResponseVo;
import com.bbex.util.DateUtils;
import com.bbex.util.JsonUtils;

import reactor.core.publisher.Mono;

public class IpLimitFilter implements WebFilter{

	private static final Logger LOGGER = LoggerFactory.getLogger(IpLimitFilter.class);

    private ServerWebExchangeMatcher requiresAuthenticationMatcher = ServerWebExchangeMatchers.anyExchange();
	
    private ValueOperations<String, String> valOpsStr;
    
    @Value("${register.limit:10}")
    private Integer limitTime ;
    
    @Value("${register.ips:}")
    private String ips ;
    
    public IpLimitFilter(ValueOperations<String, String> valOpsStr) {
    	this.valOpsStr = valOpsStr ;
    }
    
    @Override
    public Mono<Void> filter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain) {
        return this.requiresAuthenticationMatcher.matches(serverWebExchange)
                .filter(ServerWebExchangeMatcher.MatchResult::isMatch)
                .switchIfEmpty(webFilterChain.filter(serverWebExchange).then(Mono.empty()))
                .flatMap(matchResult -> {
                    //进行验证
                    final ServerHttpRequest request = serverWebExchange.getRequest();
                    String ipAddress = SecurityConfig.getIpAddress(serverWebExchange);
                    if(StringUtils.isNotBlank(ipAddress)) {
                    	int index = ipAddress.indexOf(",");
                    	if(index != -1) {
                    		if(LOGGER.isDebugEnabled()) {
                    			LOGGER.debug("获取ip列表:{},截取第一个ip",ipAddress);
                    		}
                    		ipAddress = ipAddress.split(",")[0] ;
                    	}
                    }
                    if(StringUtils.isNotBlank(ips)) {
                    	int index = ips.indexOf(",");
                    	final String validIpAddress = ipAddress ;
                    	if(index != -1) {
                    		Long count = Stream.of(ips.split(",")).distinct().filter(ip->ip.equals(validIpAddress)).count();
                    		if(count>0) {
                    			if(LOGGER.isDebugEnabled()) {
                        			LOGGER.debug("列表白名单ips:{},截取的ipAddress:{},匹配到白名单,直接通过",ips,ipAddress);
                        		}
                    			return Mono.just(true);
                    		}
                    	}else {
                    		if(ips.equals(ipAddress)) {
                    			LOGGER.debug("列表白名单ips:{},截取的ipAddress:{},匹配到白名单,直接通过",ips,ipAddress);
                    			return Mono.just(true);
                    		}
                    	}
                    }
                    String path = request.getPath().toString() ;
                    String minitorStr = DateUtils.formaterLocalDateTime(LocalDateTime.now(), DateUtils.SHORTDATEFORMAT);
                    StringBuilder keyBuilder = new StringBuilder(minitorStr+":path:"+path+":ip:"+ipAddress) ;
                    String limitKey = keyBuilder.toString() ;
                    Long requestTimes = valOpsStr.increment(limitKey,1);
                    if(LOGGER.isDebugEnabled()) {
                    	LOGGER.debug("path:{},ip:{},一天配置次数configLimit:{},当前请求访问次数reqLimit:{}",path,ipAddress,limitTime,requestTimes);
                    }
                    valOpsStr.getOperations().expire(limitKey, 3600*24+5, TimeUnit.SECONDS);
                    if(requestTimes.longValue()>limitTime) {
                    	LOGGER.error("path:{},ip:{},一天配置次数configLimit:{},当前请求访问次数reqLimit:{},请求访问次数超过限制",path,ipAddress,limitTime,requestTimes);
                    	return Mono.just(false);
                    }
                    return Mono.just(true);
                }).flatMap(results -> results ? webFilterChain.filter(serverWebExchange) : write(serverWebExchange));
    }

    private Mono<Void> write(ServerWebExchange serverWebExchange) {
        ServerHttpResponse response = serverWebExchange.getResponse();
        response.setStatusCode(HttpStatus.FORBIDDEN);
        final GlobalMessageResponseVo result = GlobalMessageResponseVo.newErrorInstance("请求被限制！");
        return response.writeWith(Mono.just(response.bufferFactory().wrap(JsonUtils.toJson(result).getBytes())));
    }

	public void setRequiresAuthenticationMatcher(ServerWebExchangeMatcher requiresAuthenticationMatcher) {
		this.requiresAuthenticationMatcher = requiresAuthenticationMatcher;
	}
	
}
